import streamlit as st
import pandas as pd
import plotly.graph_objects as go
from datetime import datetime, timedelta
import numpy as np
from prophet import Prophet
import requests
import json
from alpha_vantage.foreignexchange import ForeignExchange

# 页面配置
st.set_page_config(
    page_title="汇率预测助手",
    page_icon="💱",
    layout="wide"
)

# 自定义CSS样式
st.markdown("""
<style>
.main-header {
    text-align: center;
    padding: 1rem;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    border-radius: 10px;
    margin-bottom: 2rem;
}
.prediction-card {
    padding: 1.5rem;
    border-radius: 10px;
    background-color: #f8f9fa;
    margin: 1rem 0;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.metric-container {
    text-align: center;
    padding: 1rem;
    background-color: white;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
</style>
""", unsafe_allow_html=True)

# 页面标题
st.markdown('<div class="main-header"><h1>💱 汇率预测助手</h1></div>', unsafe_allow_html=True)

# 侧边栏配置
with st.sidebar:
    st.title("参数设置")
    
    # 货币对选择
    currency_pair = st.selectbox(
        "选择货币对",
        ["CNY/USD", "EUR/USD", "GBP/USD", "JPY/USD"],
        index=0
    )
    
    # 时间范围选择
    time_range = st.selectbox(
        "选择时间范围",
        ["1周", "1个月", "3个月", "6个月", "1年"],
        index=2
    )
    
    # 预测天数
    prediction_days = st.slider("预测天数", 1, 30, 7)

# 在文件开头添加 API 密钥配置
ALPHA_VANTAGE_API_KEY = st.secrets.get("ALPHA_VANTAGE_API_KEY", "xxxx")

@st.cache_data(ttl=3600)  # 缓存1小时
def get_exchange_rate_data(currency_pair, time_range):
    """获取真实历史汇率数据"""
    try:
        # 解析货币对
        base_currency = currency_pair.split('/')[1]  # USD
        target_currency = currency_pair.split('/')[0]  # CNY, EUR, GBP, JPY
        
        # 初始化 Alpha Vantage API
        fx = ForeignExchange(key=ALPHA_VANTAGE_API_KEY)
        
        # 获取日线数据
        data, _ = fx.get_currency_exchange_daily(
            from_symbol=base_currency,
            to_symbol=target_currency,
            outputsize='full'
        )
        
        # 转换为DataFrame
        df = pd.DataFrame.from_dict(data).T
        df.index = pd.to_datetime(df.index)
        
        # 重命名列
        df.columns = ['Open', 'High', 'Low', 'Close']
        
        # 转换数据类型
        for col in df.columns:
            df[col] = pd.to_numeric(df[col], errors='coerce')
        
        # 按时间范围筛选数据
        days_map = {
            "1周": 7,
            "1个月": 30,
            "3个月": 90,
            "6个月": 180,
            "1年": 365
        }
        days = days_map[time_range]
        end_date = datetime.now()
        start_date = end_date - timedelta(days=days)
        
        df = df[df.index >= start_date]
        
        # 获取实时汇率（用于补充最新数据）
        response = requests.get(f'https://api.exchangerate-api.com/v4/latest/{base_currency}')
        if response.status_code == 200:
            current_rate = float(response.json()['rates'][target_currency])
            
            # 添加最新数据点
            latest_date = pd.Timestamp.now().normalize()
            if latest_date not in df.index:
                latest_data = pd.DataFrame({
                    'Open': [current_rate],
                    'High': [current_rate * 1.001],
                    'Low': [current_rate * 0.999],
                    'Close': [current_rate]
                }, index=[latest_date])
                df = pd.concat([df, latest_data])
        
        # 确保数据完整性
        df = df.sort_index()
        df = df.fillna(method='ffill')
        
        if len(df) < 2:
            raise ValueError("获取的历史数据不足")
            
        st.info("使用真实历史数据")
        return df
        
    except Exception as e:
        st.error(f"获取历史数据失败: {str(e)}")
        st.info("切换到备用数据源...")
        
        # 使用备用模拟数据（保持原有的模拟数据生成逻辑）
        # ... 原有的模拟数据生成代码 ...

# 修改 create_prophet_model 函数
def create_prophet_model(data, prediction_days):
    try:
        # 调试信息
        st.write("原始数据信息:")
        st.write(f"数据形状: {data.shape}")
        st.write("数据类型:")
        st.write(data.dtypes)
        st.write("数据头部:")
        st.write(data.head())
        
        # 检查索引
        st.write("\n索引信息:")
        st.write(f"索引类型: {type(data.index)}")
        st.write(f"索引范围: {data.index.min()} 到 {data.index.max()}")
        
        # 准备Prophet数据（简化处理流程）
        df_prophet = pd.DataFrame({
            'ds': data.index,
            'y': data['Close']
        })
        
        # 确保数据类型正确
        df_prophet['ds'] = pd.to_datetime(df_prophet['ds'])
        df_prophet['y'] = df_prophet['y'].astype(float)
        
        # 检查数据有效性
        st.write("\n数据验证:")
        st.write(f"总行数: {len(df_prophet)}")
        st.write(f"空值数量: {df_prophet.isna().sum()}")
        
        # 确保数据按时间排序
        df_prophet = df_prophet.sort_values('ds')
        
        # 检查最终数据
        st.write("\n最终数据信息:")
        st.write(f"最终数据点数量: {len(df_prophet)}")
        st.write("最终数据样本:")
        st.write(df_prophet.head())
        
        # 确保至少有两行数据
        if len(df_prophet) < 2:
            raise ValueError(f"数据点数量不足，当前只有 {len(df_prophet)} 个有效数据点")
        
        # 创建和训练模型（简化参数）
        model = Prophet(
            changepoint_prior_scale=0.05,
            seasonality_mode='multiplicative',
            yearly_seasonality=False,
            weekly_seasonality=True,
            daily_seasonality=False
        )
        
        # 拟合模型
        with st.spinner("正在训练模型..."):
            model.fit(df_prophet)
        
        # 创建预测日期范围
        future_dates = model.make_future_dataframe(
            periods=prediction_days,
            freq='D'
        )
        
        # 进行预测
        forecast = model.predict(future_dates)
        
        # 预测结果信息
        st.write("\n预测结果信息:")
        st.write(f"预测天数: {len(forecast)}")
        st.write("预测样本:")
        st.write(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail())
        
        return forecast
        
    except Exception as e:
        st.error(f"预测模型错误: {str(e)}")
        st.error("详细错误信息:")
        st.exception(e)
        return None

# 主要内容区域
try:
    # 获取数据
    data = get_exchange_rate_data(currency_pair, time_range)
    
    # 显示历史走势图
    st.subheader("历史汇率走势")
    fig = go.Figure()
    fig.add_trace(go.Candlestick(
        x=data.index,
        open=data['Open'],
        high=data['High'],
        low=data['Low'],
        close=data['Close'],
        name=currency_pair
    ))
    fig.update_layout(
        xaxis_title="日期",
        yaxis_title="汇率",
        height=500
    )
    st.plotly_chart(fig, use_container_width=True)
    
    # 显示基本统计信息
    col1, col2, col3, col4 = st.columns(4)
    with col1:
        st.metric("当前汇率", f"{data['Close'][-1]:.4f}")
    with col2:
        st.metric("最高价", f"{data['High'].max():.4f}")
    with col3:
        st.metric("最低价", f"{data['Low'].min():.4f}")
    with col4:
        change = ((data['Close'][-1] - data['Close'][0]) / data['Close'][0]) * 100
        st.metric("区间涨跌幅", f"{change:.2f}%")
    
    # 训练模型并预测
    if st.button("生成预测"):
        with st.spinner("正在训练模型，请稍候..."):
            # 使用Prophet进行预测
            forecast = create_prophet_model(data, prediction_days)
            
            if forecast is not None:  # 检查预测是否成功
                # 显示预测结果
                st.subheader("预测结果")
                
                # 绘制预测图
                fig = go.Figure()
                
                # 添加历史数据
                fig.add_trace(go.Scatter(
                    x=data.index,
                    y=data['Close'],
                    name="历史数据",
                    line=dict(color="#1f77b4")
                ))
                
                # 添加预测数据
                future_dates = forecast['ds'].tail(prediction_days)
                predictions = forecast['yhat'].tail(prediction_days)
                
                fig.add_trace(go.Scatter(
                    x=future_dates,
                    y=predictions,
                    name="预测数据",
                    line=dict(color="#ff7f0e", dash='dash')
                ))
                
                # 添加预测区间
                fig.add_trace(go.Scatter(
                    x=future_dates,
                    y=forecast['yhat_upper'].tail(prediction_days),
                    fill=None,
                    mode='lines',
                    line_color='rgba(0,0,0,0)',
                    showlegend=False
                ))
                
                fig.add_trace(go.Scatter(
                    x=future_dates,
                    y=forecast['yhat_lower'].tail(prediction_days),
                    fill='tonexty',
                    mode='lines',
                    line_color='rgba(0,0,0,0)',
                    name='95% 置信区间'
                ))
                
                fig.update_layout(
                    title="汇率预测走势",
                    xaxis_title="日期",
                    yaxis_title="汇率",
                    height=400
                )
                st.plotly_chart(fig, use_container_width=True)
                
                # 生成结汇建议
                last_prediction = predictions.iloc[-1]
                pred_change = ((last_prediction - data['Close'][-1]) / data['Close'][-1]) * 100
                
                st.markdown("### 结汇建议")
                
                with st.container():
                    st.markdown(f"""
                    <div class="prediction-card">
                        <h4>预测分析：</h4>
                        <p>根据模型预测，{prediction_days}天后的汇率预计为: {last_prediction:.4f}</p>
                        <p>预期变动幅度: {pred_change:.2f}%</p>
                        
                        <h4>建议：</h4>
                        {"建议近期结汇，因为预计汇率有下跌趋势。" if pred_change < 0 else "可以观望，预计汇率有上涨空间。"}
                        
                        <h4>风险提示：</h4>
                        <p>• 本预测基于历史数据，不考虑突发事件影响</p>
                        <p>• 外汇市场受多种因素影响，预测仅供参考</p>
                        <p>• 建议结合自身需求和风险承受能力做出决策</p>
                    </div>
                    """, unsafe_allow_html=True)
            else:
                st.error("无法生成预测，请检查数据是否充足")

except Exception as e:
    st.error(f"获取数据时发生错误: {str(e)}") 